package com.black.utils.file;

import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class FileUtil
{

    public static final int BUFFER_SIZE = 8 * 1024;

    public static String buildPath(String rootPath, String... paths)
    {
        return buildPath(true, true, rootPath, paths);
    }

    public static String buildPath(boolean isCreate, String rootPath, String... paths)
    {
        return buildPath(true, isCreate, rootPath, paths);
    }

    public static String buildPath(boolean isDir, boolean isCreate, String rootPath, String... paths)
    {
        StringBuilder pathBuilder = new StringBuilder(rootPath);
        for (String path : paths)
        {
            if (path.startsWith(FileTypeEnum.PATH_SEPARATOR))
            {
                path = path.substring(1);
            }
            pathBuilder.append(path);
            if (!path.endsWith(FileTypeEnum.PATH_SEPARATOR))
            {
                pathBuilder.append(FileTypeEnum.PATH_SEPARATOR);
            }
        }
        String path = pathBuilder.toString();
        if (isDir)
        {
            File dir = new File(path);
            if (!dir.exists())
            {
                if (isCreate)
                {
                    dir.mkdirs();
                }
                else
                {
                    return null;
                }
            }
        }
        else
        {
            path.substring(0, path.length() - 1);
        }
        return path;
    }

    /**
     * @param filename 包含路径的文件名
     * @return 字节数组
     * @author Yin
     */
    public static byte[] loadFile(String filename)
        throws IOException
    {
        Path path = Paths.get(filename);
        return Files.readAllBytes(path);
    }

    public static String loadFile(String filename, Charset charset)
        throws IOException
    {
        byte[] fileContent = loadFile(filename);
        return new String(fileContent, charset);
    }

    public static byte[] loadFile(InputStream inputStream)
        throws IOException
    {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream())
        {
            // 读取所有字节
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1)
            {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
            return byteArrayOutputStream.toByteArray();
        }
        catch (IOException e)
        {
            log.error(e.getMessage());
            throw e;
        }
    }

    public static byte[] loadFile(File file)
        throws IOException
    {
        return loadFile(file.getAbsolutePath());
    }

    public static boolean saveBytes(String filePath, String fileName, byte[] data)
    {
        File dir = new File(filePath);
        if (!dir.exists())
        {
            dir.mkdirs();
        }
        File file = new File(filePath + FileTypeEnum.PATH_SEPARATOR + fileName);
        return saveBytes(file, data);
    }

    public static boolean saveBytes(File file, byte[] data)
    {
        try (FileOutputStream out = new FileOutputStream(file))
        {
            out.write(data);
            out.flush();
            return true;
        }
        catch (IOException e)
        {
            log.error(String.format("resource save failed, file :%s, error:%s", file.getAbsolutePath(), e));
        }
        return false;
    }

    public static boolean saveJson(JSONObject data, String filename, boolean isRewrite)
    {
        File datafile = new File(filename);
        if (isRewrite && datafile.exists())
        {
            datafile.delete();
        }
        return saveJson(data, filename);
    }

    public static boolean saveJson(JSONObject data, String filename)
    {
        File file = new File(filename);
        return saveJson(data, file);
    }

    public static boolean saveJson(JSONObject data, File file)
    {
        try (FileOutputStream fos = new FileOutputStream(file))
        {
            if (!file.exists())
            {
                file.createNewFile();
                file.canWrite();
            }
            fos.write(data.toJSONString().getBytes(StandardCharsets.UTF_8));
            return true;
        }
        catch (IOException e)
        {
            log.error(e.getMessage());
        }
        return false;
    }

    public static void saveFile(InputStream is, String filename)
        throws IOException
    {
        try (BufferedInputStream bis = new BufferedInputStream(is);
             BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(filename)));)
        {
            int len = -1;
            while ((len = bis.read()) != -1)
            {
                bos.write(len);
                bos.flush();
            }
        }
        catch (IOException e)
        {
            log.error(e.getMessage());
            throw e;
        }
    }

    public static byte[] toBytes(String filename)
    {
        return toBytes(new File(filename));
    }

    /**
     * 将文件转化为字节数组
     * 该方法将给定的文件读取为字节数组，主要用于需要将文件内容以字节数组形式处理的场景
     *
     * @param file 需要被读取并压缩为字节数组的文件
     * @return 文件内容对应的字节数组如果读取或压缩过程中发生错误，则返回null
     */
    public static byte[] toBytes(File file)
    {
        if (file == null || !file.exists())
        {
            log.error("File does not exist or is null: " + file);
            return null;
        }

        // 检查文件大小，避免内存溢出
        if (file.length() > Integer.MAX_VALUE)
        {
            log.error("File is too large to be processed: " + file);
            return null;
        }

        try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath())))
        {
            // 创建字节数组，大小为文件的长度
            byte[] buffer = new byte[(int)file.length()];
            // 读取文件内容到字节数组中
            int bytesRead = bis.read(buffer);
            if (bytesRead != buffer.length)
            {
                log.warn("Could not read the entire file: " + file);
            }
            return buffer;
        }
        catch (IOException e)
        {
            log.error("Error reading file: " + file, e);
            return null;
        }
    }

    public static String encodeBase64(String filename)
    {
        return encodeBase64(new File(filename));
    }

    public static String encodeBase64(File file)
    {
        try (InputStream inputStream = Files.newInputStream(file.toPath());
             BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream())
        {
            byte[] bytes = new byte[BUFFER_SIZE];
            int len;
            while ((len = bufferedInputStream.read(bytes)) != -1)
            {
                byteArrayOutputStream.write(bytes, 0, len);
            }
            byteArrayOutputStream.flush();
            String name = file.getName();
            FileTypeEnum type = FileTypeEnum.getType(name);
            return type.getDescription() + Base64.encodeBase64String(byteArrayOutputStream.toByteArray());
        }
        catch (IOException e)
        {
            log.error(e.getMessage());
        }
        return null;
    }

    public static void remove(String path)
        throws IOException
    {
        remove(new File(path));
    }

    public static void remove(Path path, boolean isDir)
        throws IOException
    {
        if (isDir)
        {
            Files.walkFileTree(path, new DeleteFileVisitor());
        }
        Files.delete(path);
    }

    public static void remove(File file)
        throws IOException
    {
        if (file.isDirectory())
        {
            remove(file.toPath(), true);
            return;
        }
        remove(file.toPath(), false);
    }

    public static long getFilesSize(List<File> files)
    {
        long size = 0;
        for (File file : files)
        {
            if (file.isFile())
            {
                size += file.length();
            }
            else if (file.isDirectory())
            {
                size += getFilesSize(Arrays.asList(file.listFiles()));
            }
        }
        return size;
    }

    private static class DeleteFileVisitor extends SimpleFileVisitor<Path>
    {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
            throws IOException
        {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
            throws IOException
        {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    }
}
